#!/usr/bin/python


from python_interface_to_soprano import *

logger = logging.getLogger("Nao")



#**********************************************************#
# This class offers the possibilities from the NAO Ontology: tags, description, rating.

class Nao:
	
	# @param interface_to_soprano an interface to call the methods : Interface
	def __init__(self, interface_to_soprano):
		self.i = interface_to_soprano

############################################################
	
	def addTag(self, uri, tag_label, tag_description="", tag_label_language=XMLSCHEMA_STRING):
		
		# if the given uri already has a tag labeled tag_label, we will not do anything
		query = "SELECT * WHERE { <" + uri + "> <" + NAO_HASTAG + "> ?tag_id ?tag_id <" + NAO_PREFLABEL + "> \"" + tag_label + "\"^^<" + XMLSCHEMA_STRING + "> }"
		existing_tag_ids = self.i.executeQuery(query)
		
		if (existing_tag_ids.next()):
			raise CanNotException("The resource \"" + uri + "\" already has a tag named \"" + tag_label + "\".")
		
		existing_tag_ids.close()
		
		# the following points have been written paying attention to the NEPOMUK Annotation Ontology (NAO) standards, which can be found at http://www.semanticdesktop.org/ontologies/2007/08/15/nao/#2.3._Tagging_as_Annotation on the 2008-06-09
		
		# 1. create a new tag id to identify the new nao:Tag
		tag_id = self.i.getUniqueId()
		
		# 2. write the information about that tag in the database: rdf:type, nao:created, nao:prefLabel, nao:description
		
		# 2.1. rdf:type
		#		[tag_id, rdf:type, nao:tag, ?]
		error_code = self.i.registerStatement(tag_id, RDF_TYPE, NAO_TAG)
		# print error_code
		
		# 2.2. nao:created
		#		[tag_id, nao:created, date, ?]
		# TODO: that date is not well registered in the database... find out why!
		date = strftime("%Y-%m-%dT%H:%M:%S%Z") # see http://www.w3.org/TR/xmlschema-2/#dateTime for details about the standard format, and http://docs.python.org/lib/module-time.html for the python strftime function and others
		error_code = self.i.registerStatement(tag_id, NAO_CREATED, date, XMLSCHEMA_DATETIME)
		#print statement
		# print error_code
		
		# 2.3. nao:prefLabel
		#		[tag_id, nao:prefLabel, tag_label, ?]
		error_code = self.i.registerStatement(tag_id, NAO_PREFLABEL, tag_label, tag_label_language)
		# print error_code
		
		# 2.4. nao:description
		#		[tag_id, nao:description, tag_description, ?]
		error_code = self.i.registerStatement(tag_id, NAO_DESCRIPTION, tag_description, XMLSCHEMA_STRING)
		# print error_code
		
		# 3. double-link that nao:Tag with the corresponding resource (given uri)
		
		# 3.1. one way
		#		[uri, nao:hasTag, tag_id, ?]
		error_code = self.i.registerStatement(uri, NAO_HASTAG, tag_id)
		# print error_code
		
		# 3.2. and the other
		#		[tag_id, nao:isTagFor, uri, ?]
		error_code = self.i.registerStatement(tag_id, NAO_ISTAGFOR, uri)
		# print error_code
		
		# 4. write the information about the uri in the database: type, url (parts of Xesam)
		# TODO: has it to be done ?
		
		return 0
		
############################################################
	
	def removeTag(self, uri, tag_label, tag_label_language=XMLSCHEMA_STRING):
		
		# remove every information attached to that tag
		
		# 1. find the tag id
		query = "SELECT * WHERE { ?tag_id <" + NAO_PREFLABEL + "> \"" + tag_label + "\"^^<" + XMLSCHEMA_STRING + "> <" + uri + "> <" + NAO_HASTAG + "> ?tag_id }"
		possible_tag_ids = self.i.executeQuery(query)
		tag_id = None
		if (possible_tag_ids.next()):
			tag_id = possible_tag_ids.current()[0]["tag_id"][1]
		else:
			possible_tag_ids.close()
			raise CanNotException("Not any tag labeled \""+tag_label+"\" for uri \""+uri+"\".")
		
		# there should be only one result
		if (possible_tag_ids.next()): # there is more than one result!
			possible_tag_ids.close()
			raise DatabaseIncoherenceException("Database incoherence: there is more than one tag_id for the same tag of " + uri + "!")
		
		possible_tag_ids.close()
		
		# delete all the statements concerning this tag
		
		# 2.1. [tag_id, rdf:type, nao:tag, ?]
		#print "2.1"
		error_code = self.i.deleteStatement(tag_id, RDF_TYPE, NAO_TAG)
		# print error_code
		
		#print "2.3"
		# 2.3. [tag_id, nao:prefLabel, tag_label, ?]
		error_code = self.i.deleteStatement(tag_id, NAO_PREFLABEL, tag_label, tag_label_language)
		# print error_code
		
		#print "3.1"
		# 3.1. [uri, nao:hasTag, tag_id, ?]
		error_code = self.i.deleteStatement(uri, NAO_HASTAG, tag_id)
		# print error_code
		
		#print "3.2"
		# 3.2. [tag_id, nao:isTagFor, uri, ?]
		error_code = self.i.deleteStatement(tag_id, NAO_ISTAGFOR, uri)
		# print error_code
		
		#print "2.2"
		# 2.2. [tag_id, nao:created, date (unknown here), ?]
		error_code = self.i.deleteAllStatements(tag_id, NAO_CREATED, "?")
		# print error_code
		
		#print "2.4"
		# 2.4. [tag_id, nao:description, tag_description (unknown here), ?]
		error_code = self.i.deleteAllStatements(tag_id, NAO_DESCRIPTION, "?")
		# print error_code
		
		#print "4."
		# 4. ?
		
		return 0
		
############################################################

	# Returns the description of the given tag (uri - tag label) or "no description"
	def getDescriptionOfTag(self, uri, tag_label, tag_label_language=XMLSCHEMA_STRING):
		
		# 1. find the tag id
		query = "SELECT * WHERE { ?tag_id <" + NAO_PREFLABEL + "> \"" + tag_label + "\"^^<" + XMLSCHEMA_STRING + "> <" + uri + "> <" + NAO_HASTAG + "> ?tag_id }"
		possible_tag_ids = self.i.executeQuery(query)
		
		tag_id = None
		if (possible_tag_ids.next()):
			tag_id = possible_tag_ids.current()[0]["tag_id"][1]
		else:
			return "no description"
		
		# there should be only one result
		if (possible_tag_ids.next()): # there is more than one result!
			possible_tag_ids.close()
			raise DatabaseIncoherenceException("Database incoherence: there is more than one tag_id for the same tag of " + uri + "!")
		
		possible_tag_ids.close()
		
		description = self.i.getImage(tag_id, NAO_DESCRIPTION)
		
		if description == None:
			# TODO: error? (DatabaseIncoherence...)
			return "no description"
		else:
			return description

############################################################

	# writes the "output" file tagsCache.rdf, to be used as a source for a XUL treeview.
	def getTagsOf(self, uri):
		
		query = "SELECT DISTINCT ?uri ?tag ?desc WHERE { ?uri <" + NAO_HASTAG + "> ?tag_id ?tag_id <" + NAO_PREFLABEL + "> ?tag ?tag_id <" + NAO_DESCRIPTION + "> ?desc FILTER (str(?uri) = \"" + uri + "\") }"
		tags = self.i.executeQuery(query)
		
		gen = rdf_gen(self.i.results_path+"/tagsCache.rdf")
		gen.write_tags(tags)
		
		tags.close()
		
		return 0
		
############################################################

	# writes the "output" file tagsCache.rdf, to be used as a source for a XUL treeview.
	def getAllTags(self):
		
		query = "SELECT DISTINCT ?uri ?tag ?desc WHERE { ?uri <" + NAO_HASTAG + "> ?tag_id ?tag_id <" + NAO_PREFLABEL + "> ?tag ?tag_id <" + NAO_DESCRIPTION + "> ?desc }"
		tags = self.i.executeQuery(query)
		
		gen = rdf_gen(self.i.results_path+"/tagsCache.rdf")
		gen.write_tags(tags)
		
		tags.close()
		
		return 0
		
############################################################

	def setDescription(self, uri, description):
		
		# as described at http://www.semanticdesktop.org/ontologies/2007/08/15/nao/#2.2.10._nao:description (available on the 2008-06-08), "The maximum cardinality [of a nao:description] is 1".
		# so we first delete the (possibly) existing description before adding the new one.
		self.removeDescription(uri)
		
		# and then set the given description
		error_code = self.i.registerStatement(uri, NAO_DESCRIPTION, description, XMLSCHEMA_STRING)
		return error_code
		
############################################################
	
	def removeDescription(self, uri):
		
		error_code = self.i.deleteAllStatements(uri, NAO_DESCRIPTION, "?")
		return error_code
		
############################################################
	
	# Returns the string description of the given uri.
	def getDescriptionOf(self, uri):
		
		query = "SELECT ?desc WHERE { <" + uri + "> <" + NAO_DESCRIPTION + "> ?desc }"
		descriptions = self.i.executeQuery(query)
		
		description = None
		if descriptions.next():
			description = descriptions.current()[0]["desc"][1]
		else:
			description = "no description"
		
		if descriptions.next():
			descriptions.close()
			raise DatabaseIncoherenceException("Database incoherence: there is more than one description for \"" + uri + "\"")
		
		descriptions.close()
		
		return description
		
############################################################

	# param value is passed as a string but represents a number.
	# TODO: for right now, only integer numbers are allowed.
	# Sets the numeric rating of the given URI to value.
	def setNumericRating(self, uri, value):
		
		# as described at http://www.semanticdesktop.org/ontologies/2007/08/15/nao/#numericRating (available on the 2008-06-11), nao:numericRating has "Maximal cardinality [=] 1".
		# so we first delete the (possibly) existing numericRating before adding the new one.
		self.removeNumericRating(uri)
		
		# and then set the rating to the given value
		error_code = self.i.registerStatement(uri, NAO_NUMERICRATING, value, XMLSCHEMA_DECIMAL)
		return error_code
	
############################################################

	# Removes the numeric rating for this URI.
	def removeNumericRating(self, uri):
		
		error_code = self.i.deleteAllStatements(uri, NAO_NUMERICRATING, "?")
		return error_code

############################################################

	# Get the numeric value for this URI.
	def getNumericRating(self, uri):
		
		value = self.i.getImage(uri, NAO_NUMERICRATING)
		
		if value == None:
			return 0
		else:
			# convert value:string into an integer
			return int(value)



